using System;
using System.Reflection;
using System.Reflection.Emit;
using RaisingStudio.SmallProgram.Library;
using RaisingStudio.SmallProgram.Library.Internal;
using RaisingStudio.SmallBasicCompiler.Statements;

namespace RaisingStudio.SmallBasicCompiler
{
    public class CodeGenerator
    {
        private string _outputName;
        private string _directory;
        private Parser _parser;
        private MethodInfo _entryPoint;
        private CodeGenScope _currentScope;
        private TypeInfoBag _typeInfoBag;
        private SymbolTable _symbolTable;

        public CodeGenerator(Parser parser, TypeInfoBag typeInfoBag, string outputName, string directory)
        {
            if (parser == null)
            {
                throw new ArgumentNullException("parser");
            }
            if (typeInfoBag == null)
            {
                throw new ArgumentNullException("typeInfoBag");
            }
            this._parser = parser;
            this._symbolTable = this._parser.SymbolTable;
            this._typeInfoBag = typeInfoBag;
            this._outputName = outputName;
            this._directory = directory;
        }

        public bool GenerateExecutable()
        {
            AssemblyName assemblyName = new AssemblyName();
            assemblyName.Name = this._outputName;
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save, this._directory);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(this._outputName + ".exe", true);
            if (!this.EmitModule(moduleBuilder))
            {
                return false;
            }
            assemblyBuilder.SetEntryPoint(this._entryPoint, PEFileKinds.WindowApplication);
            assemblyBuilder.Save(this._outputName + ".exe");
            return true;
        }

        private bool EmitModule(ModuleBuilder moduleBuilder)
        {
            TypeBuilder typeBuilder = moduleBuilder.DefineType("_SmallBasicProgram", TypeAttributes.Sealed);
            MethodBuilder methodBuilder = typeBuilder.DefineMethod("_Main", MethodAttributes.Static);
            this._entryPoint = methodBuilder;
            ILGenerator iLGenerator = methodBuilder.GetILGenerator();
            this._currentScope = new CodeGenScope
            {
                ILGenerator = iLGenerator,
                MethodBuilder = methodBuilder,
                TypeBuilder = typeBuilder,
                SymbolTable = this._symbolTable,
                TypeInfoBag = this._typeInfoBag
            };
            this.BuildFields(typeBuilder);
            iLGenerator.EmitCall(OpCodes.Call, typeof(SmallProgramApplication).GetMethod("BeginProgram", BindingFlags.Static | BindingFlags.Public), null);
            this.EmitIL();
            iLGenerator.EmitCall(OpCodes.Call, typeof(TextWindow).GetMethod("PauseIfVisible", BindingFlags.Static | BindingFlags.Public), null);
            iLGenerator.EmitCall(OpCodes.Call, typeof(SmallProgramApplication).GetMethod("EndProgram", BindingFlags.Static | BindingFlags.Public), null);
            iLGenerator.Emit(OpCodes.Ret);
            typeBuilder.CreateType();
            return true;
        }

        private void BuildFields(TypeBuilder typeBuilder)
        {
            SymbolTable symbolTable = this._parser.SymbolTable;
            foreach (string current in symbolTable.Variables.Keys)
            {
                FieldInfo value = typeBuilder.DefineField(current, typeof(Primitive), FieldAttributes.Private | FieldAttributes.Static);
                this._currentScope.Fields.Add(current, value);
            }
        }

        private void EmitIL()
        {
            foreach (Statement current in this._parser.ParseTree)
            {
                current.PrepareForEmit(this._currentScope);
            }
            foreach (Statement current2 in this._parser.ParseTree)
            {
                current2.EmitIL(this._currentScope);
            }
        }
    }
}
